import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.Timer;

@SuppressWarnings("serial")
public class Board3 extends JPanel implements ActionListener {

	private int WIDTH = 300;				// board width
	private int HEIGHT = 300;				// board height
	private int DELAY = 140;				// controls the speed of the snake
	private final int DOT_SIZE = 10;		// spacing between the snake
	private final int ALL_DOTS = 900;
	private final int RAND_POS = 29;
	private int bgcolor = 0;
	private int level = 0;
	private int score = 0;

	private int x[] = new int[ALL_DOTS];
	private int y[] = new int[ALL_DOTS];

	private int dots;
	private int apple_x;
	private int apple_y;

	private boolean left = false;
	private boolean right = true;
	private boolean up = false;
	private boolean down = false;
	private boolean inGame = true;

	private Timer timer;
	private Image ball;
	private Image apple;
	private Image head;

	private Image w1;    //image of a wall
	private Image w2;
	private Image w3;
	private Image w4;
	private Image w5;
	
	private int w1_x;    //variable wall_x used to keep track of random position
	private int w1_y;    //variable wall_y used to keep track of random position
	private int w2_x;
	private int w2_y;
	private int w3_x;
	private int w3_y;
	private int w4_x;
	private int w4_y;
	private int w5_x;
	private int w5_y;
		
	private Image ob1;
	private Image ob2;
	private Image ob3;
	private Image ob4;
	private Image ob5;
	private Image ob6;
	private Image ob7;
	private Image ob8;
	private Image ob9;
	private Image ob10;
	
	private int ob1_x;
	private int ob1_y;
	private int ob2_x;
	private int ob2_y;
	private int ob3_x;
	private int ob3_y;
	private int ob4_x;
	private int ob4_y;
	private int ob5_x;
	private int ob5_y;
	private int ob6_x;
	private int ob6_y;
	private int ob7_x;
	private int ob7_y;
	private int ob8_x;
	private int ob8_y;
	private int ob9_x;
	private int ob9_y;
	private int ob10_x;
	private int ob10_y;

	private Image portal;    //image of a portal
	private int portal_x;    //variable portal_x used to keep track of random position
	private int portal_y;    //variable portal_y used to keep track of random position
	
	private Image portalExit;
	private int portalExit_x;
	private int portalExit_y;
	
	private Image portal2;    //image of a portal
	private int portal2_x;    //variable portal_x used to keep track of random position
	private int portal2_y;    //variable portal_y used to keep track of random position
	
	private Image portalExit2;
	private int portalExit2_x;
	private int portalExit2_y;
	
	/**
	 * Creates a new board, and places all the pieces into there places.
	 * - the snake, moving wall, and apple
	 */
    public Board3(int width, int height, int speed, int difficulty) 
    {
    	System.out.println("b.height: " + HEIGHT);
    	this.HEIGHT = height;
    	System.out.println("a.height: " + HEIGHT);
    	System.out.println("b.width: " + WIDTH);
    	this.WIDTH = width;
    	System.out.println("a.width: " + WIDTH);
    	System.out.println("b.speed: " + DELAY);
    	this.DELAY = speed;
    	System.out.println("b.speed: " + DELAY);

		addKeyListener(new TAdapter());

		/**
		 * By using different colors, we need to change the boarders of the pictures.
		 * For now, I'm leaving it with this color, just to see what we are working with
		 * if you want to comment the code out and use the above, "original code" that's
		 * totally fine with me.
		 * 
		 * My goal is that, I'm hoping to have different pictures in the backgrounds such as:
		 * - forests, deserts, water, etc.
		 * 
		 * ---Jordan, below is a list of already inputed valid colors, there is an option to 
		 *    there is an option to create your own colors, do you want to give the users that
		 *    option?
		 */
		switch(bgcolor)
		{
		case 1:
			setBackground(Color.cyan);
			break;
		case 2:
			setBackground(Color.blue);
			break;
		case 3:
			setBackground(Color.darkGray);
			break;
		case 4:
			setBackground(Color.gray);
			break;
		case 5:
			setBackground(Color.green);
			break;
		case 6:
			setBackground(Color.lightGray);
			break;
		case 7:
			setBackground(Color.magenta);
			break;
		case 8:
			setBackground(Color.orange);
			break;
		case 9:
			setBackground(Color.pink);
			break;
		case 10:
			setBackground(Color.red);
			break;
		case 11:
			setBackground(Color.white);
			break;
		case 12:
			setBackground(Color.yellow);
			break;
		case 13:
			setBackground(Color.black);
			break;
		default:
			setBackground(Color.black);
			break;
		}

		ImageIcon iid = new ImageIcon(this.getClass().getResource("dot.png"));
		ball = iid.getImage();

		ImageIcon iia = new ImageIcon(this.getClass().getResource("apple.png"));
		apple = iia.getImage();

		ImageIcon iih = new ImageIcon(this.getClass().getResource("head.png"));
		head = iih.getImage();

		/**
		 * This code places the moving wall on the board
		 */
		ImageIcon iiw = new ImageIcon(this.getClass().getResource("wall.png"));
		w1 = iiw.getImage();
		
		ImageIcon iiw2 = new ImageIcon(this.getClass().getResource("wall2.png"));
		w2 = iiw2.getImage();
		
		ImageIcon iiw3 = new ImageIcon(this.getClass().getResource("wall3.png"));
		w3 = iiw3.getImage();
		
		ImageIcon iiw4 = new ImageIcon(this.getClass().getResource("wall4.png"));
		w4 = iiw4.getImage();
		
		ImageIcon iiw5 = new ImageIcon(this.getClass().getResource("wall5.png"));
		w5 = iiw5.getImage();
		
		ImageIcon iiob1 = new ImageIcon(this.getClass().getResource("obstacle1.png"));
		ob1 = iiob1.getImage();
		
		ImageIcon iiob2 = new ImageIcon(this.getClass().getResource("obstacle2.png"));
		ob2 = iiob2.getImage();
		
		ImageIcon iiob3 = new ImageIcon(this.getClass().getResource("obstacle3.png"));
		ob3 = iiob3.getImage();
		
		ImageIcon iiob4 = new ImageIcon(this.getClass().getResource("obstacle4.png"));
		ob4 = iiob4.getImage();
		
		ImageIcon iiob5 = new ImageIcon(this.getClass().getResource("obstacle5.png"));
		ob5 = iiob5.getImage();
		
		ImageIcon iiob6 = new ImageIcon(this.getClass().getResource("obstacle6.png"));
		ob6 = iiob6.getImage();
		
		ImageIcon iiob7 = new ImageIcon(this.getClass().getResource("obstacle7.png"));
		ob7 = iiob7.getImage();
		
		ImageIcon iiob8 = new ImageIcon(this.getClass().getResource("obstacle8.png"));
		ob8 = iiob8.getImage();
		
		ImageIcon iiob9 = new ImageIcon(this.getClass().getResource("obstacle9.png"));
		ob9 = iiob9.getImage();
		
		ImageIcon iiob10 = new ImageIcon(this.getClass().getResource("obstacle10.png"));
		ob10 = iiob10.getImage();
		
		/**
		 * This code places a portal on the board
		 */
		ImageIcon iip = new ImageIcon(this.getClass().getResource("portal.png"));
		portal = iip.getImage();
		
		ImageIcon iipe = new ImageIcon(this.getClass().getResource("portalExit.png"));
		portalExit = iipe.getImage();

		ImageIcon iip2 = new ImageIcon(this.getClass().getResource("portal2.png"));
		portal2 = iip2.getImage();
		
		ImageIcon iipe2 = new ImageIcon(this.getClass().getResource("portalExit2.png"));
		portalExit2 = iipe2.getImage();
		
		setFocusable(true);
		initGame();
	}


	/**
	 * Initiates the game with:
	 * - snake = length3
	 * - where the snake starts
	 * - places a apple in a random position on the map
	 * - places a moving wall in a random position on the map
	 */
	public void initGame() {
		dots = 3;

		//the map is currently a 300x300 map
		for (int z = 0; z < dots; z++) {
			x[z] = 50 - z*10; //width or horizontal axis
			y[z] = 50;        //height or vertical axis
		}

		locateApple();        //places an apple, randomly on the map
		locateW1();         //places a wall, randomly on the map
		locateW2();
		locateW3();
		locateW4();
		locateW5();
		locateOb1();
		locateOb2();
		locateOb3();
		locateOb4();
		locateOb5();
		locateOb6();
		locateOb7();
		locateOb8();
		locateOb9();
		locateOb10();
		locatePortal();       //places a portal, randomly on the map
		locatePortalExit();
		locatePortal2();
		locatePortalExit2();

		timer = new Timer(DELAY, this);
		timer.start();
	}

	
	/**
	 * Paints the different images onto the board:
	 * - apples
	 * - moving walls
	 * - game over screen
	 */
	public void paint(Graphics g) {
		super.paint(g);

		if (inGame) {
			g.drawImage(apple, apple_x, apple_y, this);    //creates a new apple image
			g.drawImage(w1, w1_x, w1_y, this);       //creates a new wall image
			g.drawImage(w2, w2_x, w2_y, this);       //creates a new wall image
			g.drawImage(w3, w3_x, w3_y, this);       //creates a new wall image
			g.drawImage(w4, w4_x, w4_y, this);       //creates a new wall image
			g.drawImage(w5, w5_x, w5_y, this);       //creates a new wall image
			g.drawImage(ob1, ob1_x, ob1_x, this);
			g.drawImage(ob2, ob2_x, ob2_x, this);
			g.drawImage(ob3, ob3_x, ob3_x, this);
			g.drawImage(ob4, ob4_x, ob4_x, this);
			g.drawImage(ob5, ob5_x, ob5_x, this);
			g.drawImage(ob6, ob6_x, ob6_x, this);
			g.drawImage(ob7, ob7_x, ob7_x, this);
			g.drawImage(ob8, ob8_x, ob8_x, this);
			g.drawImage(ob9, ob9_x, ob9_x, this);
			g.drawImage(ob10, ob10_x, ob10_x, this);
			g.drawImage(portal, portal_x, portal_y, this); //creates a new portal image
			g.drawImage(portalExit, portalExit_x, portalExit_y, this);
			g.drawImage(portal2, portal2_x, portal2_y, this); //creates a new portal image
			g.drawImage(portalExit2, portalExit2_x, portalExit2_y, this);
			

			//chooses a random spot on the map to place the images
			for (int z = 0; z < dots; z++) {
				if (z == 0)
					g.drawImage(head, x[z], y[z], this);
				else g.drawImage(ball, x[z], y[z], this);
			}

			Toolkit.getDefaultToolkit().sync();
			g.dispose();

		} else {
			gameOver(g);
		}
	}


	/**
	 * Creates the game over screen, once the game is over.
	 */
	public void gameOver(Graphics g) {
		String msg = "Game Over";
		Font small = new Font("Helvetica", Font.BOLD, 14);
		FontMetrics metr = this.getFontMetrics(small);

		g.setColor(Color.white);
		g.setFont(small);
		g.drawString(msg, (WIDTH - metr.stringWidth(msg)) / 2, HEIGHT / 2);
		
		Scores.addNewScore(score, level);
	}


	/**
	 * If apple is eaten:
	 * - increase dots (size of snake by 1)
	 * - place another apple in a random spot
	 * - place another moving wall in a random spot
	 */
	public void checkApple() {
		if ((x[0] == apple_x) && (y[0] == apple_y)) {
			dots++;        //increments the size of the snake
			locateApple(); //places a new apple
			locateW1();  //places a new wall
			locateW2();
			locateW3();
			locateW4();
			locateW5();
			locateOb1();
			locateOb2();
			locateOb3();
			locateOb4();
			locateOb5();
			locateOb6();
			locateOb7();
			locateOb8();
			locateOb9();
			locateOb10();
			locatePortal();//places a new portal
			locatePortalExit();
			locatePortal2();
			locatePortalExit2();
		}
	}


	/**
	 * Takes input in order to move the snake.
	 */
	public void move() {
		for (int z = dots; z > 0; z--) {
			x[z] = x[(z - 1)];
			y[z] = y[(z - 1)];
		}

		if (left) {
			x[0] -= DOT_SIZE;
		}

		if (right) {
			x[0] += DOT_SIZE;
		}

		if (up) {
			y[0] -= DOT_SIZE;
		}

		if (down) {
			y[0] += DOT_SIZE;
		}
	}


	/**
	 * Checks to see if the snake has hit obstacle or wall, and if so
	 * the game is over.
	 */
	public void checkCollision() {
		
		//checks if snake hits itself
		for (int z = dots; z > 0; z--) {
			if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) {
				inGame = false;
			}
		}

		//next 4 if's determine boarder
		if (y[0] > HEIGHT) {
			inGame = false;
		}

		if (y[0] < 0) {
			inGame = false;
		}

		if (x[0] > WIDTH) {
			inGame = false;
		}

		if (x[0] < 0) {
			inGame = false;
		}
	}

	
	/**
	 * Method that puts an apple in a random position on the map
	 */
	public void locateApple() {
		int r = (int) (Math.random() * RAND_POS);
		apple_x = ((r * DOT_SIZE));
		r = (int) (Math.random() * RAND_POS);
		apple_y = ((r * DOT_SIZE));
	}


	/**
	 * Method that puts a moving wall in a random position on the map
	 */
	public void locateW1() {
		int r = (int) (Math.random() * RAND_POS);
		w1_x = ((r * DOT_SIZE));
		r = (int) (Math.random() * RAND_POS);
		w1_y = ((r * DOT_SIZE));
	}

	/**
	 * If moving wall is hit, the game is over.
	 */
	public void checkW1() {
		if ((x[0] == w1_x) && (y[0] == w1_y)) {
			inGame = false;
		}
	}

	/**
	 * Method that puts a moving wall in a random position on the map
	 */
	public void locateW2() {
		int r = (int) (Math.random() * RAND_POS);
		w2_x = ((r * DOT_SIZE));
		r = (int) (Math.random() * RAND_POS);
		w2_y = ((r * DOT_SIZE));
	}

	/**
	 * If moving wall is hit, the game is over.
	 */
	public void checkW2() {
		if ((x[0] == w2_x) && (y[0] == w2_y)) {
			inGame = false;
		}
	}
	
	/**
	 * Method that puts a moving wall in a random position on the map
	 */
	public void locateW3() {
		int r = (int) (Math.random() * RAND_POS);
		w3_x = ((r * DOT_SIZE));
		r = (int) (Math.random() * RAND_POS);
		w3_y = ((r * DOT_SIZE));
	}

	/**
	 * If moving wall is hit, the game is over.
	 */
	public void checkW3() {
		if ((x[0] == w3_x) && (y[0] == w3_y)) {
			inGame = false;
		}
	}
	
	/**
	 * Method that puts a moving wall in a random position on the map
	 */
	public void locateW4() {
		int r = (int) (Math.random() * RAND_POS);
		w4_x = ((r * DOT_SIZE));
		r = (int) (Math.random() * RAND_POS);
		w4_y = ((r * DOT_SIZE));
	}

	/**
	 * If moving wall is hit, the game is over.
	 */
	public void checkW4() {
		if ((x[0] == w4_x) && (y[0] == w4_y)) {
			inGame = false;
		}
	}
	
	/**
	 * Method that puts a moving wall in a random position on the map
	 */
	public void locateW5() {
		int r = (int) (Math.random() * RAND_POS);
		w5_x = ((r * DOT_SIZE));
		r = (int) (Math.random() * RAND_POS);
		w5_y = ((r * DOT_SIZE));
	}

	/**
	 * If moving wall is hit, the game is over.
	 */
	public void checkW5() {
		if ((x[0] == w5_x) && (y[0] == w5_y)) {
			inGame = false;
		}
	}
	
	public void locateOb1() {
		ob1_x = 45;
		ob1_y = 150;
	}
	
	public void checkOb1() {
		if ((x[0] == ob1_x) && (y[0] == ob1_y)) {
			inGame = false;
		}
	}
	
	public void locateOb2() {
		ob2_x = 46;
		ob2_y = 150;
	}
	
	public void checkOb2() {
		if ((x[0] == ob2_x) && (y[0] == ob2_y)) {
			inGame = false;
		}
	}
	
	public void locateOb3() {
		ob3_x = 47;
		ob3_y = 150;
	}
	
	public void checkOb3() {
		if ((x[0] == ob3_x) && (y[0] == ob3_y)) {
			inGame = false;
		}
	}
	
	public void locateOb4() {
		ob4_x = 48;
		ob4_y = 150;
	}
	
	public void checkOb4() {
		if ((x[0] == ob4_x) && (y[0] == ob4_y)) {
			inGame = false;
		}
	}
	
	public void locateOb5() {
		ob5_x = 49;
		ob5_y = 150;
	}
	
	public void checkOb5() {
		if ((x[0] == ob5_x) && (y[0] == ob5_y)) {
			inGame = false;
		}
	}
	
	public void locateOb6() {
		ob6_x = 50;
		ob6_y = 150;
	}
	
	public void checkOb6() {
		if ((x[0] == ob6_x) && (y[0] == ob6_y)) {
			inGame = false;
		}
	}
	
	public void locateOb7() {
		ob7_x = 51;
		ob7_y = 150;
	}
	
	public void checkOb7() {
		if ((x[0] == ob7_x) && (y[0] == ob7_y)) {
			inGame = false;
		}
	}
	
	public void locateOb8() {
		ob8_x = 52;
		ob8_y = 150;
	}
	
	public void checkOb8() {
		if ((x[0] == ob8_x) && (y[0] == ob8_y)) {
			inGame = false;
		}
	}
	
	public void locateOb9() {
		ob9_x = 53;
		ob9_y = 150;
	}
	
	public void checkOb9() {
		if ((x[0] == ob9_x) && (y[0] == ob9_y)) {
			inGame = false;
		}
	}
	
	public void locateOb10() {
		ob10_x = 54;
		ob10_y = 150;
	}
	
	public void checkOb10() {
		if ((x[0] == ob10_x) && (y[0] == ob10_y)) {
			inGame = false;
		}
	}
	
	/**
	 * Method that puts a portal in a random position on the map
	 */
	public void locatePortal() {
		int r = (int) (Math.random() * RAND_POS);
		portal_x = ((r * DOT_SIZE));
		r = (int) (Math.random() * RAND_POS);
		portal_y = ((r * DOT_SIZE));
	}

	/**
	 * When portal is entered, send snake somewhere else
	 * Currently the snake is set to teleport to the original spot it starts in
	 * It also calls a new apple, wall and portal locations
	 */
	public void checkPortal() {
		if ((x[0] == portal_x) && (y[0] == portal_y)) {
;
			for (int z = 0; z < dots; z++) {
				x[z] = 50 - z*10; //width or horizontal axis
				y[z] = 50;        //height or vertical axis
			}

			locatePortal();       //places a portal, randomly on the map
			locatePortalExit();

		}
	}
	
	/**
	 * Method that puts a portal in a random position on the map
	 */
	public void locatePortal2() {
		int r = (int) (Math.random() * RAND_POS);
		portal2_x = ((r * DOT_SIZE));
		r = (int) (Math.random() * RAND_POS);
		portal2_y = ((r * DOT_SIZE));
	}

	/**
	 * When portal is entered, send snake somewhere else
	 * Currently the snake is set to teleport to the original spot it starts in
	 * It also calls a new apple, wall and portal locations
	 */
	public void checkPortal2() {
		if ((x[0] == portal2_x) && (y[0] == portal2_y)) {

			//***********REED: possibly change this (self-reminder)
			for (int z = 0; z < dots; z++) {
				x[z] = 50 - z*10; //width or horizontal axis
				y[z] = 50;        //height or vertical axis
			}

			locatePortal2();       //places a portal, randomly on the map
			locatePortalExit2();

		}
	}
	
	
	public void locatePortalExit() {
		portalExit_x = 150;//((r * DOT_SIZE));
		portalExit_y = 50;//((r * DOT_SIZE));
	}
	
	public void checkPortalExit() {
		if ((x[0] == portalExit_x) && (y[0] == portalExit_y)) {
			for (int z = 0; z < dots; z++) {
				x[z] = 150 - z*10; //width or horizontal axis
				y[z] = 50;        //height or vertical axis
			}
		}
	}
	
	
	public void locatePortalExit2() {
		portalExit2_x = 250;//((r * DOT_SIZE));
		portalExit2_y = 50;//((r * DOT_SIZE));
	}
	
	public void checkPortalExit2() {
		if ((x[0] == portalExit2_x) && (y[0] == portalExit2_y)) {
			for (int z = 0; z < dots; z++) {
				x[z] = 250 - z*10; //width or horizontal axis
				y[z] = 50;        //height or vertical axis
			}
		}
	}
	
	/**
	 * Checks the actions performed, makes sure that game is still going.
	 */
	public void actionPerformed(ActionEvent e) {
		if (inGame) {
			checkApple();
			checkW1();
			checkW2();
			checkW3();
			checkW4();
			checkW5();
			checkOb1();
			checkOb2();
			checkOb3();
			checkOb4();
			checkOb5();
			checkOb6();
			checkOb7();
			checkOb8();
			checkOb9();
			checkOb10();
			checkPortal();
			checkPortalExit();
			checkCollision();
			move();

			/**
			 * By adding move() == makes the game speed up exponentially.
			 * This could be used as a setting "speed up snake"
			 */
			//move();
		}
		repaint();
	}


	/**
	 * Getter used to check if snake game should still be running
	 * @return
	 */
	public boolean isInGame() {
		return inGame;
	}


	/**
	 * Setter used to change if snake game should still be running
	 * @param inGame
	 */
	public void setInGame(boolean inGame) {
		this.inGame = inGame;
	}


	/**
	 * This Method starts up the whole snake GUI.
	 * @author Jordan B. & Reed P.
	 */
	private class TAdapter extends KeyAdapter {
		public void keyPressed(KeyEvent e) {
			int key = e.getKeyCode();

			if ((key == KeyEvent.VK_LEFT) && (!right)) {
				left = true;
				up = false;
				down = false;
			}

			if ((key == KeyEvent.VK_RIGHT) && (!left)) {
				right = true;
				up = false;
				down = false;
			}

			if ((key == KeyEvent.VK_UP) && (!down)) {
				up = true;
				right = false;
				left = false;
			}

			if ((key == KeyEvent.VK_DOWN) && (!up)) {
				down = true;
				right = false;
				left = false;
			}
		}
	}
}